/************************************************************
  * @brief   button_drive
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0.1
  * @note    button.c
  ***********************************************************/

#include "button.h"
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "serial.h"
/*******************************************************************
 *                          Variable Declaration                              
 *******************************************************************/
Button_t Button1,Button2,Button3,Button4,Button5;
Button_t Button_Encoder;
static struct button* Head_Button = NULL;

unsigned char Read_Button1_Level(void)
{
	return HAL_GPIO_ReadPin(IN_KEY1_PORT,IN_KEY1_PIN);
}
unsigned char Read_Button2_Level(void)
{
	return HAL_GPIO_ReadPin(IN_KEY2_PORT,IN_KEY2_PIN);
}
unsigned char Read_Button3_Level(void)
{
	return HAL_GPIO_ReadPin(IN_KEY3_PORT,IN_KEY3_PIN);
}
unsigned char Read_Button4_Level(void)
{
	return HAL_GPIO_ReadPin(IN_KEY4_PORT,IN_KEY4_PIN);
}
unsigned char Read_Button5_Level(void)
{
	return HAL_GPIO_ReadPin(IN_KEY5_PORT,IN_KEY5_PIN);
}
unsigned char Read_ButtonEncoderPush_Level(void)
{
	return HAL_GPIO_ReadPin(CODEC_PUSH_PORT,CODEC_PUSH_PIN);
}

void Btn_Down_ControlLed(void)
{
	HAL_GPIO_WritePin(LED_CTRL2_PORT, LED_CTRL2_PIN,HAL_GPIO_ReadPin(IN_KEY1_PORT,IN_KEY1_PIN));
	HAL_GPIO_WritePin(LED_CTRL3_PORT, LED_CTRL3_PIN,HAL_GPIO_ReadPin(IN_KEY2_PORT,IN_KEY2_PIN));
	HAL_GPIO_WritePin(LED_CTRL4_PORT, LED_CTRL4_PIN,HAL_GPIO_ReadPin(IN_KEY3_PORT,IN_KEY3_PIN));
	HAL_GPIO_WritePin(LED_CTRL5_PORT, LED_CTRL5_PIN,HAL_GPIO_ReadPin(IN_KEY4_PORT,IN_KEY4_PIN));
	HAL_GPIO_WritePin(LED_CTRL6_PORT, LED_CTRL6_PIN,HAL_GPIO_ReadPin(IN_KEY5_PORT,IN_KEY5_PIN));
	HAL_GPIO_WritePin(CODEC_LEDCTRL1_PORT, CODEC_LEDCTRL1_PIN,HAL_GPIO_ReadPin(CODEC_PUSH_PORT,CODEC_PUSH_PIN));
	HAL_GPIO_WritePin(CODEC_LEDCTRL2_PORT, CODEC_LEDCTRL2_PIN,HAL_GPIO_ReadPin(CODEC_PUSH_PORT,CODEC_PUSH_PIN));
}

void Btn_Down_CallBack(void *btn)
{
	SerialTx_InsertBuff("abcdefghijk",10);

	//HAL_UART_Transmit_IT(&huart1,Uart1_Txbuf,1);
	if(btn == &Button1)
	{
			HAL_GPIO_WritePin(LED_CTRL2_PORT, LED_CTRL2_PIN,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(RUN_LED_PORT, RUN_LED_PIN,GPIO_PIN_SET);

	}
	if(btn == &Button2)
	{
			HAL_GPIO_WritePin(LED_CTRL3_PORT, LED_CTRL3_PIN,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(RUN_LED_PORT, RUN_LED_PIN,GPIO_PIN_SET);
	}	
	if(btn == &Button3)
	{
			HAL_GPIO_WritePin(LED_CTRL4_PORT, LED_CTRL4_PIN,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(RUN_LED_PORT, RUN_LED_PIN,GPIO_PIN_SET);

	}	
	if(btn == &Button4)
	{
			HAL_GPIO_WritePin(LED_CTRL5_PORT, LED_CTRL5_PIN,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(RUN_LED_PORT, RUN_LED_PIN,GPIO_PIN_SET);
				
	}	
	if(btn == &Button5)
	{
			HAL_GPIO_WritePin(LED_CTRL6_PORT, LED_CTRL6_PIN,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(RUN_LED_PORT, RUN_LED_PIN,GPIO_PIN_SET);
		
	}	
	if(btn == &Button_Encoder)
	{
			HAL_GPIO_WritePin(CODEC_LEDCTRL1_PORT, CODEC_LEDCTRL1_PIN,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(CODEC_LEDCTRL2_PORT, CODEC_LEDCTRL2_PIN,GPIO_PIN_RESET);
		
			HAL_GPIO_WritePin(RUN_LED_PORT, RUN_LED_PIN,GPIO_PIN_SET);

	}
}
void Btn_Double_CallBack(void *btn)
{
		SerialTx_InsertBuff("ddddddddddd",10);

}
void Btn_Long_CallBack(void *btn)
{
		SerialTx_InsertBuff("llllllllllllllll",10);

}
void Button_Init(void)
{	
	Button_Create("Button1", &Button1, Read_Button1_Level,0);               
	Button_Create("Button2", &Button2, Read_Button2_Level,0);              
	Button_Create("Button3", &Button3, Read_Button3_Level,0);               
	Button_Create("Button4", &Button4, Read_Button4_Level,0);               
	Button_Create("Button5", &Button5, Read_Button5_Level,0);               
	Button_Create("EncoderPush", &Button_Encoder, Read_ButtonEncoderPush_Level,0);               

	Button_Attach(&Button1,BUTTON_DOWM,Btn_Down_CallBack);        
	Button_Attach(&Button1,BUTTON_DOUBLE,Btn_Double_CallBack);    
	Button_Attach(&Button1,BUTTON_LONG,Btn_Long_CallBack);        
	
	Button_Attach(&Button2,BUTTON_DOWM,Btn_Down_CallBack);        
	Button_Attach(&Button2,BUTTON_DOUBLE,Btn_Double_CallBack);    
	Button_Attach(&Button2,BUTTON_LONG,Btn_Long_CallBack);        //
	
	Button_Attach(&Button3,BUTTON_DOWM,Btn_Down_CallBack);        
	Button_Attach(&Button3,BUTTON_DOUBLE,Btn_Double_CallBack);    
	Button_Attach(&Button3,BUTTON_LONG,Btn_Long_CallBack);        //
	
	Button_Attach(&Button4,BUTTON_DOWM,Btn_Down_CallBack);        
	Button_Attach(&Button4,BUTTON_DOUBLE,Btn_Double_CallBack);    
	Button_Attach(&Button4,BUTTON_LONG,Btn_Long_CallBack);        //
	
	Button_Attach(&Button5,BUTTON_DOWM,Btn_Down_CallBack);        
	Button_Attach(&Button5,BUTTON_DOUBLE,Btn_Double_CallBack);    
	Button_Attach(&Button5,BUTTON_LONG,Btn_Long_CallBack);        //
	
	Button_Attach(&Button_Encoder,BUTTON_DOWM,Btn_Down_CallBack);        
	Button_Attach(&Button_Encoder,BUTTON_DOUBLE,Btn_Double_CallBack);    
	Button_Attach(&Button_Encoder,BUTTON_LONG,Btn_Long_CallBack);        //
}


/*******************************************************************
 *                         Function Declaration   
 *******************************************************************/
static char *StrnCopy(char *dst, const char *src, unsigned int n);
static void Print_Btn_Info(Button_t* btn);
static void Add_Button(Button_t* btn);


/************************************************************
  * @brief   Create a Button 
  * @param   name:button name 
  * @param   btn:button structure
  * @param   read_btn_level:Button trigger level reading function,
  *                 Return the level of the unsigned char type by yourself
  * @param   btn_trigger_level:Button trigger level
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    NULL
  ***********************************************************/
void Button_Create(const char *name,
                  Button_t *btn, 
                  unsigned char(*read_btn_level)(void),
                  unsigned char btn_trigger_level)
{
  if( btn == NULL)
  {
  }
  
  memset(btn, 0, sizeof(struct button));      //Clear structure information
 
  StrnCopy(btn->Name, name, BTN_NAME_MAX);    //button name 
  
  
  btn->Button_State = NONE_TRIGGER;                     //Button status
  btn->Button_Last_State = NONE_TRIGGER;                //Button last status
  btn->Button_Trigger_Event = NONE_TRIGGER;             //Button trigger event
  btn->Read_Button_Level = read_btn_level;              //Button trigger level reading function
  btn->Button_Trigger_Level = btn_trigger_level;        //Button trigger level
  btn->Button_Last_Level = btn->Read_Button_Level();    //Button current level
  btn->Debounce_Time = 0;
  
  
  Add_Button(btn);          //Added to the singly linked list when button created
  
  Print_Btn_Info(btn);      //printf info
 
}

/************************************************************
  * @brief   burron trigger events are attach to callback function
  * @param   btn:button structure
  * @param   btn_event:button events
  * @param   btn_callback : Callback handler after the button is triggered.Need user implementation
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  ***********************************************************/
void Button_Attach(Button_t *btn,Button_Event btn_event,Button_CallBack btn_callback)
{
  if( btn == NULL)
  {
  }
  
  if(BUTTON_ALL_RIGGER == btn_event)
  {
    for(unsigned char i = 0 ; i < number_of_event-1 ; i++)
      /*A callback function triggered by a button event ,Used to handle button events */
      btn->CallBack_Function[i] = btn_callback;   
  }
  else
  {
    btn->CallBack_Function[btn_event] = btn_callback; 
  }
}

/************************************************************
  * @brief   Delete an already created button
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    NULL
  ***********************************************************/
void Button_Delete(Button_t *btn)
{
  struct button** curr;
  for(curr = &Head_Button; *curr;) 
  {
    struct button* entry = *curr;
    if (entry == btn) 
    {
      *curr = entry->Next;
    } 
    else
    {
      curr = &entry->Next;
    }
  }
}

/************************************************************
  * @brief   Get Button Event Info
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  ***********************************************************/
void Get_Button_EventInfo(Button_t *btn)
{
  for(unsigned char i = 0 ; i < number_of_event-1 ; i++)
  {
    if(btn->CallBack_Function[i] != 0)
    {
      /* print */
    }      
  } 
}

/************************************************************
  * @brief   Get Button Event
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  ***********************************************************/
unsigned char Get_Button_Event(Button_t *btn)
{
  return (unsigned char)(btn->Button_Trigger_Event);
}

/************************************************************
  * @brief   Get Button State
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  ***********************************************************/
unsigned char Get_Button_State(Button_t *btn)
{
  return (unsigned char)(btn->Button_State);
}

/************************************************************
  * @brief   button cycle processing function
  * @param   btn:button structure
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    This function must be called in a certain period. The recommended period is 20~50ms.
  ***********************************************************/
void Button_Cycle_Process(Button_t *btn)
{
  /* Get the current button level */
  unsigned char current_level = (unsigned char)btn->Read_Button_Level();
  
  /* Button level changes, debounce */
  if((current_level != btn->Button_Last_Level)&&(++(btn->Debounce_Time) >= BUTTON_DEBOUNCE_TIME))
  {
      /* Update current button level */
      btn->Button_Last_Level = current_level;

      /* button is pressed */
      btn->Debounce_Time = 0;
      
      /* If the button is not pressed, change the button state to press (first press / double trigger) */
      if((btn->Button_State == NONE_TRIGGER)||(btn->Button_State == BUTTON_DOUBLE))
      {
        btn->Button_State = BUTTON_DOWM;
      }
      //free button
      else if(btn->Button_State == BUTTON_DOWM)
      {
        btn->Button_State = BUTTON_UP;
      }
  }
  
  switch(btn->Button_State)
  {
    /* button dowm */
    case BUTTON_DOWM :                                        
    {
      if(btn->Button_Last_Level == btn->Button_Trigger_Level) 
      {
        /* Support continuous triggering */
        #ifdef CONTINUOS_TRIGGER                              

        if(++(btn->Button_Cycle) >= BUTTON_CONTINUOS_CYCLE)
        {
          btn->Button_Cycle = 0;
          btn->Button_Trigger_Event = BUTTON_CONTINUOS; 
          /* continuous triggering */
          TRIGGER_CB(BUTTON_CONTINUOS);                      
        }
        
        #else
        
        btn->Button_Trigger_Event = BUTTON_DOWM;
        
        /* Update the trigger event before releasing the button as long press */
        if(++(btn->Long_Time) >= BUTTON_LONG_TIME)            
        {
          #ifdef LONG_FREE_TRIGGER
          
          btn->Button_Trigger_Event = BUTTON_LONG; 
          
          #else
          
          /* Continuous triggering of long press cycles */
          if(++(btn->Button_Cycle) >= BUTTON_LONG_CYCLE)      
          {
            btn->Button_Cycle = 0;
            btn->Button_Trigger_Event = BUTTON_LONG; 
            
            /* long triggering */
            TRIGGER_CB(BUTTON_LONG);    
          }
          #endif
          
          /* Update time overflow */
          if(btn->Long_Time == 0xFF)
          {
            btn->Long_Time = BUTTON_LONG_TIME;
          }
        }
          
        #endif
      }

      break;
    } 
    
    /* button up */
    case BUTTON_UP :
    {
      /* Trigger click */
      if(btn->Button_Trigger_Event == BUTTON_DOWM)          
      {
        /* double click */
        if((btn->Timer_Count <= BUTTON_DOUBLE_TIME)&&(btn->Button_Last_State == BUTTON_DOUBLE)) 
        {
          btn->Button_Trigger_Event = BUTTON_DOUBLE;
          TRIGGER_CB(BUTTON_DOUBLE);    
          btn->Button_State = NONE_TRIGGER;
          btn->Button_Last_State = NONE_TRIGGER;
        }
        else
        {
            btn->Timer_Count=0;
            /* Detection long press failed, clear 0 */
            btn->Long_Time = 0;
          
          #ifndef SINGLE_AND_DOUBLE_TRIGGER

             /* click */
            TRIGGER_CB(BUTTON_DOWM);
          #endif
            btn->Button_State = BUTTON_DOUBLE;
            btn->Button_Last_State = BUTTON_DOUBLE;
          
        }
      }
      
      else if(btn->Button_Trigger_Event == BUTTON_LONG)
      {
        #ifdef LONG_FREE_TRIGGER
          /* Long press */
          TRIGGER_CB(BUTTON_LONG);
        #else
          
          /* Long press free */
          TRIGGER_CB(BUTTON_LONG_FREE);
        #endif
        btn->Long_Time = 0;
        btn->Button_State = NONE_TRIGGER;
        btn->Button_Last_State = BUTTON_LONG;
      } 
      
      #ifdef CONTINUOS_TRIGGER
        /* Press continuously */
        else if(btn->Button_Trigger_Event == BUTTON_CONTINUOS)  
        {
          btn->Long_Time = 0;
           /* Press continuously free */
          TRIGGER_CB(BUTTON_CONTINUOS_FREE);
          btn->Button_State = NONE_TRIGGER;
          btn->Button_Last_State = BUTTON_CONTINUOS;
        } 
      #endif
      
      break;
    }
    
    case BUTTON_DOUBLE :
    {
      /* Update time */
      btn->Timer_Count++;                                      
      if(btn->Timer_Count>=BUTTON_DOUBLE_TIME)
      {
        btn->Button_State = NONE_TRIGGER;
        btn->Button_Last_State = NONE_TRIGGER;
      }
      #ifdef SINGLE_AND_DOUBLE_TRIGGER
      
        if((btn->Timer_Count>=BUTTON_DOUBLE_TIME)&&(btn->Button_Last_State != BUTTON_DOWM))
        {
          btn->Timer_Count=0;
          TRIGGER_CB(BUTTON_DOWM);    
          btn->Button_State = NONE_TRIGGER;
          btn->Button_Last_State = BUTTON_DOWM;
        }
        
      #endif

      break;
    }

    default :
      break;
  }
  
}

/************************************************************
  * @brief   Traversing the way to scan the button without losing each button
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    This function is called periodically, it is recommended to call 20-50ms once.
  ***********************************************************/
void Button_Process(void)
{
  struct button* pass_btn;
  for(pass_btn = Head_Button; pass_btn != NULL; pass_btn = pass_btn->Next)
  {
      Button_Cycle_Process(pass_btn);
  }
}

/************************************************************
  * @brief   Search Button
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    NULL
  ***********************************************************/
void Search_Button(void)
{
  struct button* pass_btn;
  for(pass_btn = Head_Button; pass_btn != NULL; pass_btn = pass_btn->Next)
  {
  }
}

/************************************************************
  * @brief   Handle all button callback functions
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    Not implemented yet
  ***********************************************************/
void Button_Process_CallBack(void *btn)
{
  unsigned char btn_event = Get_Button_Event(btn);

  switch(btn_event)
  {
    case BUTTON_DOWM:
    {
      break;
    }
    
    case BUTTON_UP:
    {
      break;
    }
    
    case BUTTON_DOUBLE:
    {
      break;
    }
    
    case BUTTON_LONG:
    {
      break;
    }
    
    case BUTTON_LONG_FREE:
    {
      break;
    }
    
    case BUTTON_CONTINUOS:
    {
      break;
    }
    
    case BUTTON_CONTINUOS_FREE:
    {
      break;
    }
      
  } 
}


/**************************** The following is the internal call function ********************/

/************************************************************
  * @brief   Copy the specified length string
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    NULL
  ***********************************************************/
static char *StrnCopy(char *dst, const char *src, unsigned int n)
{
  if (n != 0)
  {
    char *d = dst;
    const char *s = src;
    do
    {
        if ((*d++ = *s++) == 0)
        {
            while (--n != 0)
                *d++ = 0;
            break;
        }
    } while (--n != 0);
  }
  return (dst);
}

/************************************************************
  * @brief   Print button related information
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    NULL
  ***********************************************************/
static void Print_Btn_Info(Button_t* btn)
{
  

  Search_Button();
}
/************************************************************
  * @brief   Connect buttons with a single linked list
  * @param   NULL
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    NULL
  ***********************************************************/
static void Add_Button(Button_t* btn)
{
  struct button *pass_btn = Head_Button;
  
  while(pass_btn)
  {
    pass_btn = pass_btn->Next;
  }
  
  btn->Next = Head_Button;
  Head_Button = btn;
}




